import M from '../../general/utils';
import AabbIntersects from './aabb-intersects';
import Node from './node';

/**
 * 轴向扫描检测相交
 */
export default class SweepAndPrune extends AabbIntersects{
    constructor(values={}){
        super(values);
    }

    /**
     * 添加形状
     * @param collider {Collider}
     */
    addCollider(collider){
        this._nodeLength++;
        this._createNode(collider);
    }

    /**
     * 更新形状
     * @param colliders
     */
    updateCollider(...colliders){
        this._clearPairs();
        let count=0;
        for(let collider of colliders){
            let nodeIndex=collider.userData.nodeIndex;
            this._updateDataList(nodeIndex,collider.box);
            this._updateList[count]=nodeIndex;
            count++;
        }
        this._updateList[count]=-1;
        this._axisOrder();
        if(this._setMainAxisData()) {
            for (let i = 0; i < this._updateList.length; i++) {
                let cIndex=this._updateList[i];
                if (cIndex < 0)break;
                this._testNode(cIndex);
            }
        }
    }

    /**
     * 移除形状
     * @param collider
     */
    removeCollider(collider){
        this._nodeLength--;
        let nodeIndex=collider.userData.nodeIndex;

        this._sum[0]-=this._boxList[nodeIndex*6];
        this._sum[1]-=this._boxList[nodeIndex*6+1];
        this._sum[2]-=this._boxList[nodeIndex*6+2];

        this._dv[0]-=this._dvList[nodeIndex*3];
        this._dv[1]-=this._dvList[nodeIndex*3+1];
        this._dv[2]-=this._dvList[nodeIndex*3+2];

        this._removeNodeFromList(nodeIndex);
        collider.userData.nodeIndex=null;
    }

    /**
     * 测试节点是否相交并记录交叠对
     * @param cIndex
     * @private
     */
    _testNode(cIndex){
        if(this._sweepSubordinateAxis(cIndex)){
            let start=false;
            let cIndex6=cIndex*6;
            let tar6=0;
            for(let j=0;j<this._refCList.length;j++){
                let tar=this._refCList[j];
                if(tar<0)break;
                if(tar===cIndex){
                    if(!start){
                        start=true;
                    }
                }else{
                    if(!start) {
                        tar6=tar*6;
                        if(this._boxList[tar6+this._offset[5]]>this._boxList[cIndex6+this._offset[4]]){
                            this._recordPair(cIndex, tar);
                        }
                        continue;
                    }
                    tar6=tar*6;
                    if(this._boxList[tar6+this._offset[4]]>this._boxList[cIndex6+this._offset[5]])break;
                    this._recordPair(cIndex, tar);
                }
            }
        }
    }

    /**
     * 扫掠从属轴
     * @param nodeIndex
     * @returns {boolean}
     * @private
     */
    _sweepSubordinateAxis(nodeIndex){
        if(this._setSecondAxisData(nodeIndex)){
            return this._setThirdAxisData(nodeIndex);
        }
        return false;
    }

    /**
     * 设置第三轴数据列表
     * @param nodeIndex
     * @returns {boolean}
     * @private
     */
    _setThirdAxisData(nodeIndex){
        let begin=false;
        let count=0;
        let node6=nodeIndex*6;
        let tar6=0;
        for(let i=0;i<this._refBList.length;i++){
            let tar=this._refBList[i];
            if(tar<0)break;
            if(nodeIndex===tar){
                if(!begin){
                    count=this._setListData(count,nodeIndex,node6,this._refCList,this._axisCList,this._offset[4]);
                    begin=true;
                }
            }else{
                if(!begin){
                    tar6=tar*6;
                    if(this._boxList[tar6+this._offset[3]]>this._boxList[node6+this._offset[2]]){
                        count=this._setListData(count,tar,tar6,this._refCList,this._axisCList,this._offset[4]);
                    }
                    continue;
                }
                tar6=tar*6;
                if(this._boxList[tar6+this._offset[2]]>this._boxList[node6+this._offset[3]])break;
                count=this._setListData(count,tar,tar6,this._refCList,this._axisCList,this._offset[4]);
            }
        }
        if(count<2)return false;
        this._refCList[count]=-2;
        SweepAndPrune.speedSort(this._axisCList,this._refCList,count-1);
        return true;
    }

    /**
     * 设置第二轴数据列表
     * @param nodeIndex
     * @returns {boolean}
     * @private
     */
    _setSecondAxisData(nodeIndex){
        let begin=false;
        let count=0;
        let node6=nodeIndex*6;
        let tar6=0;
        for(let i=0;i<this._refAList.length;i++){
            let tar=this._refAList[i];
            if(tar<0)break;
            if(nodeIndex===tar){
                if(!begin){
                    count=this._setListData(count,nodeIndex,node6,this._refBList,this._axisBList,this._offset[2]);
                    begin=true;
                }
            }else{
                if(!begin){
                    tar6=tar*6;
                    if(this._boxList[tar6+this._offset[1]]>this._boxList[node6+this._offset[0]]){
                        count=this._setListData(count,tar,tar6,this._refBList,this._axisBList,this._offset[2]);
                    }
                    continue;
                }
                tar6=tar*6;
                if(this._boxList[tar6+this._offset[0]]>this._boxList[node6+this._offset[1]])break;
                count=this._setListData(count,tar,tar6,this._refBList,this._axisBList,this._offset[2]);
            }
        }
        if(count<2)return false;
        this._refBList[count]=-2;
        SweepAndPrune.speedSort(this._axisBList,this._refBList,count-1);
        return true;
    }

    /**
     * 设置主轴数据列表
     * @returns {boolean}
     * @private
     */
    _setMainAxisData(){
        let count=0;
        for(let i=0;i<this._nodesList.length;i++){
            if(this._nodesList[i]===0)break;
            if(this._nodesList[i]>1){
                count=this._setListData(count,i,i*6,this._refAList,this._axisAList,this._offset[0]);
            }
        }
        if(count<2)return false;
        this._refAList[count]=-2;
        SweepAndPrune.speedSort(this._axisAList,this._refAList,count-1);
        return true;
    }

    /**
     * 设置列表数据
     * @param count
     * @param index
     * @param index6
     * @param refList
     * @param axisList
     * @param offsetMin
     * @returns {SweepAndPrune._setListData.props|*}
     * @private
     */
    _setListData(count,index,index6,refList,axisList,offsetMin){
        let c=count;
        refList[c]=index;
        axisList[c]=this._boxList[index6+offsetMin];
        c++;
        return c;
    }

    /**
     * 设置轴序
     * @private
     */
    _axisOrder(){
        if(this._dv[0]>=this._dv[1]&&this._dv[0]>=this._dv[2]){
            if(this._dv[1]>=this._dv[2]){
                this._offset[0]=0;
                this._offset[1]=3;
                this._offset[2]=1;
                this._offset[3]=4;
                this._offset[4]=2;
                this._offset[5]=5;
            }else{
                this._offset[0]=0;
                this._offset[1]=3;
                this._offset[2]=2;
                this._offset[3]=5;
                this._offset[4]=1;
                this._offset[5]=4;
            }
        }
        if(this._dv[1]>=this._dv[0]&&this._dv[1]>=this._dv[2]){
            if(this._dv[0]>=this._dv[2]){
                this._offset[0]=1;
                this._offset[1]=4;
                this._offset[2]=0;
                this._offset[3]=3;
                this._offset[4]=2;
                this._offset[5]=5;
            }else{
                this._offset[0]=1;
                this._offset[1]=4;
                this._offset[2]=2;
                this._offset[3]=5;
                this._offset[4]=0;
                this._offset[5]=3;
            }
        }
        if(this._dv[2]>=this._dv[1]&&this._dv[2]>=this._dv[0]){
            if(this._dv[1]>=this._dv[0]){
                this._offset[0]=2;
                this._offset[1]=5;
                this._offset[2]=1;
                this._offset[3]=4;
                this._offset[4]=0;
                this._offset[5]=3;
            }else{
                this._offset[0]=2;
                this._offset[1]=5;
                this._offset[2]=0;
                this._offset[3]=3;
                this._offset[4]=1;
                this._offset[5]=4;
            }
        }
    }

    /**
     * 更新节点对应的列表数据
     * @param nodeIndex
     * @param box
     * @private
     */
    _updateDataList(nodeIndex,box){
        this._dv[0]-=this._dvList[nodeIndex*3];
        this._dv[1]-=this._dvList[nodeIndex*3+1];
        this._dv[2]-=this._dvList[nodeIndex*3+2];

        this._sum[0]-=this._boxList[nodeIndex*6];
        this._sum[1]-=this._boxList[nodeIndex*6+1];
        this._sum[2]-=this._boxList[nodeIndex*6+2];

        this._boxList[nodeIndex*6]=box.min.x;
        this._boxList[nodeIndex*6+1]=box.min.y;
        this._boxList[nodeIndex*6+2]=box.min.z;
        this._boxList[nodeIndex*6+3]=box.max.x;
        this._boxList[nodeIndex*6+4]=box.max.y;
        this._boxList[nodeIndex*6+5]=box.max.z;

        this._sum[0]+=this._boxList[nodeIndex*6];
        let avg=this._sum[0]/this._nodeLength;
        let dvg=avg-this._boxList[nodeIndex*6];
        this._dvList[nodeIndex*3]=dvg*dvg;
        this._dv[0]+=this._dvList[nodeIndex*3];

        this._sum[1]+=this._boxList[nodeIndex*6+1];
        avg=this._sum[1]/this._nodeLength;
        dvg=avg-this._boxList[nodeIndex*6+1];
        this._dvList[nodeIndex*3+1]=dvg*dvg;
        this._dv[1]+=this._dvList[nodeIndex*3+1];

        this._sum[2]+=this._boxList[nodeIndex*6+2];
        avg=this._sum[2]/this._nodeLength;
        dvg=avg-this._boxList[nodeIndex*6+2];
        this._dvList[nodeIndex*3+2]=dvg*dvg;
        this._dv[2]+=this._dvList[nodeIndex*3+2];
    }

    /**
     * 从列表中移除节点
     * @param nodeIndex
     * @private
     */
    _removeNodeFromList(nodeIndex){
        this._disableListNode(nodeIndex);
        this._nodes[nodeIndex]=null;
    }

    /**
     * 创建新的节点
     * @param collider
     * @returns {node}
     * @private
     */
    _createNode(collider){
        let node=new Node();
        node.collider=collider;

        let index=0;
        for(let i=0;i<this._nodesList.length;i++){
            if(this._nodesList[i]<2){
                index=i;
                break;
            }
        }

        if(collider)collider.userData.nodeIndex=index;
        node.index=index;
        this._nodes[index]=node;
        this._enableListNode(index);
        this._checkVolume();
        this._recordNodeBox(index,collider.box);
        return node;
    }

    /**
     * 记录节点包围盒数据
     * @param nodeIndex
     * @param box
     * @private
     */
    _recordNodeBox(nodeIndex,box){
        this._boxList[nodeIndex*6]=box.min.x;
        this._boxList[nodeIndex*6+1]=box.min.y;
        this._boxList[nodeIndex*6+2]=box.min.z;
        this._boxList[nodeIndex*6+3]=box.max.x;
        this._boxList[nodeIndex*6+4]=box.max.y;
        this._boxList[nodeIndex*6+5]=box.max.z;

        this._sum[0]+=this._boxList[nodeIndex*6];
        let avg=this._sum[0]/this._nodeLength;
        let dvg=avg-this._boxList[nodeIndex*6];
        this._dvList[nodeIndex*3]=dvg*dvg;
        this._dv[0]+=this._dvList[nodeIndex*3];

        this._sum[1]+=this._boxList[nodeIndex*6+1];
        avg=this._sum[1]/this._nodeLength;
        dvg=avg-this._boxList[nodeIndex*6+1];
        this._dvList[nodeIndex*3+1]=dvg*dvg;
        this._dv[1]+=this._dvList[nodeIndex*3+1];

        this._sum[2]+=this._boxList[nodeIndex*6+2];
        avg=this._sum[2]/this._nodeLength;
        dvg=avg-this._boxList[nodeIndex*6+2];
        this._dvList[nodeIndex*3+2]=dvg*dvg;
        this._dv[2]+=this._dvList[nodeIndex*3+2];
    }

    /**
     * 检查列表容量并增加列表容量
     * @private
     */
    _checkVolume(){
        if(this._nodes.length>this._volume*0.8){
            this._volume*=2;
            this._resizeList();
        }
    }

    /**
     * 增加列表容量
     * @private
     */
    _resizeList(){
        this._nodesList=M.resizeTypedArray(this._nodesList,this._volume);
        this._boxList=M.resizeTypedArray(this._boxList,this._volume*6);
        this._dvList=M.resizeTypedArray(this._dvList,this._volume*3);
        this._axisAList=M.resizeTypedArray(this._axisAList,this._volume);
        this._axisBList=M.resizeTypedArray(this._axisBList,this._volume);
        this._axisCList=M.resizeTypedArray(this._axisCList,this._volume);
        this._refAList=M.resizeTypedArray(this._refAList,this._volume);
        this._refBList=M.resizeTypedArray(this._refBList,this._volume);
        this._refCList=M.resizeTypedArray(this._refCList,this._volume);
        this._updateList=M.resizeTypedArray(this._updateList,this._volume);
    }

    /**
     * 标记列表节点位置不可用
     * @param nodeIndex
     * @private
     */
    _disableListNode(nodeIndex){
        this._nodesList[nodeIndex]=1;
    }

    /**
     * 标记列表节点位置为可用
     * @param nodeIndex
     * @private
     */
    _enableListNode(nodeIndex){
        this._nodesList[nodeIndex]=2;
    }

    /**
     * 清除交叠对记录
     * @private
     */
    _clearPairs(){
        if(!this._overlaps)return;
        this._overlaps.clear();
    }

    /**
     * 记录交叠对
     * @param nodeAIndex
     * @param nodeBIndex
     * @private
     */
    _recordPair(nodeAIndex,nodeBIndex){
        if(!this._overlaps)return;
        this._overlaps.addPair(nodeAIndex,nodeBIndex);
    }

    /**
     * 快速排序递归
     * @param i
     * @param j
     * @param array
     * @param indexRef
     */
    static sortIn(i,j,array,indexRef){
        if(i === j ) return;
        let ra=indexRef[i];
        let va = array[i];
        let si = i;
        let sj = j;
        while(j > i){
            if(array[j] >= va){
                j--;
            }else{
                array[i] = array[j];
                indexRef[i]=indexRef[j];
                while(j > ++i){
                    if(array[i] > va){
                        array[j] = array[i];
                        indexRef[j]=indexRef[i];
                        break;
                    }
                }
            }
        }
        if(si === i){
            SweepAndPrune.sortIn(++i, sj,array,indexRef);
            return ;
        }
        array[i] = va;
        indexRef[i]=ra;
        SweepAndPrune.sortIn(si, i,array,indexRef);
        SweepAndPrune.sortIn(j, sj,array,indexRef);
    }

    /**
     * 快速排序
     * @param array
     * @param indexRef
     * @param breakPoint
     */
    static speedSort(array,indexRef,breakPoint=null){
        let i = 0;
        let j = breakPoint===null?array.length - 1:breakPoint;
        SweepAndPrune.sortIn(i, j,array,indexRef);
    }

    _initVariable(){
        super._initVariable();
        this._nodeLength=0;
        this._boxList=new Float32Array(this._volume*6);//[min.x,min.y,min.z,max.x,max.y,max.z]
        this._sum=new Float32Array(3);
        this._dv=new Float32Array(3);
        this._dvList=new Float32Array(this._volume*3);
        this._axisAList=new Float32Array(this._volume);
        this._axisBList=new Float32Array(this._volume);
        this._axisCList=new Float32Array(this._volume);
        this._refAList=new Int16Array(this._volume);//-1:空位,-2:断点
        this._refAList[0]=-2;
        this._refBList=new Int16Array(this._volume);
        this._refBList[0]=-2;
        this._refCList=new Int16Array(this._volume);
        this._refCList[0]=-2;

        this._offset=new Int8Array(6);

        this._updateList=new Int16Array(this._volume);//-1:断点
    }
}